package com.kly.pay.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.fqgj.common.api.Response;
import com.fqgj.exception.common.ApplicationException;
import com.fqgj.exception.enums.BasicMsgCodeEnum;
import com.google.common.collect.Maps;
import com.google.common.eventbus.AsyncEventBus;
import com.kly.enums.*;
import com.kly.order.db.dao.PSubscriptionLogDao;
import com.kly.order.db.entity.PSubscriptionLogEntity;
import com.kly.pay.db.dao.POrderDao;
import com.kly.pay.db.dao.PReceiveNotifyLogDao;
import com.kly.pay.db.entity.POrderEntity;
import com.kly.pay.db.entity.PReceiveNotifyLogEntity;
import com.kly.pay.eventbus.Event;
import com.kly.pay.factory.GoogleApiService;
import com.kly.pay.factory.PayServiceFactory;
import com.kly.pay.service.BasicPayActionService;
import com.kly.req.ReceiveNotifyRequest;
import com.kly.req.RepayRequest;
import com.kly.req.RepayStatusRequest;
import com.kly.rsp.RepayResponse;
import com.kly.rsp.RepayStatusResponse;
import com.kly.service.pay.RepayService;
import com.kly.util.PaySnowflake;
import com.kly.util.ValidateParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 支付处理类
 * @author linqu
 */
@Service("repayService")
@Slf4j
public class RepayServiceImpl implements RepayService {

    @Resource
    PayServiceFactory payServiceFactory;
    @Resource
    AsyncEventBus asyncEventBus;
    @Resource
    POrderDao pOrderDao;
    @Resource
    PReceiveNotifyLogDao receiveNotifyLogDao;
    @Resource
    PSubscriptionLogDao subscriptionLogDao;

    /**
     * 资金流入
     * @param repayRequest
     * @return
     */
    @Override
    public Response<RepayResponse> repay(RepayRequest repayRequest) {
        log.info("[PaymentLog] RepayService.repay begin, userCode:{}, orderNo:{} repayRequest:{}", repayRequest.getUserCode(), repayRequest.getOrderNo(), JSONObject.toJSONString(repayRequest));

        // 参数检查
        validateRepayParams(repayRequest);

        // 日志记录
        asyncEventBus.post(buildEvent(repayRequest));

        // 检查orderNo在p_order中是否存在记当，以避免重复支付操作
        final String payOrderNo = validateExistsOrderNo(repayRequest);

        final BasicPayActionService basicPayActionService = getBasicPayActionService(repayRequest);
        final RepayResponse result = basicPayActionService.repay(payOrderNo, repayRequest);
        if (Objects.isNull(result)) {
            log.error("[PaymentLog] InvokerRepayFailed, params={}" , JSONObject.toJSONString(repayRequest));
            return Response.error("Invoker repay failed, params=" + JSONObject.toJSONString(repayRequest));
        }
        return Response.ok(result);
    }

    /**
     * 检查是否存在订单记录，如果存在，不允许重复发起入账操作，
     * 以避免同一步订单重复发起支付资金流入操作
     */
    private String validateExistsOrderNo(RepayRequest repayRequest) {
        final POrderEntity payOrder = pOrderDao.queryByOrderNoAndPayType(repayRequest.getOrderNo(), repayRequest.getPayType());
        // 检查是否存在订单记录，如果存在，不允许重复发起入账操作
        if (Objects.nonNull(payOrder)) {
//            throw new ApplicationException("[PaymentLog] TheOrderHasExistsInRecords, orderNo:" + repayRequest.getOrderNo());
            return payOrder.getPayOrderNo();
        }

        // 若未存在订单，新增一笔支付订单记录
        POrderEntity entity = addPayOrderEntity(repayRequest);

        return entity.getPayOrderNo();
    }

    private POrderEntity queryByOrderNo(RepayStatusRequest repayStatusRequest) {
        final POrderEntity payOrder = pOrderDao.queryByOrderNoAndPayType(repayStatusRequest.getOrderNo(), String.valueOf(PayTypeEnum.REPAY.getCode()));
        // 检查是否存在订单记录，如果存在，不允许重复发起入账操作
        if (Objects.isNull(payOrder)) {
            throw new ApplicationException("[PaymentLog] TheOrderNotExistsInRecords, orderNo:" + repayStatusRequest.getOrderNo());
        }
        return payOrder;
    }

    /**
     * 添加支付订单记录
     */
    private POrderEntity addPayOrderEntity(RepayRequest repayRequest) {
        POrderEntity entity = new POrderEntity();
        final String payExt = repayRequest.getPayExt();
        if (!StringUtils.isEmpty(payExt)) {
            final JSONObject payExtJson = JSONObject.parseObject(payExt);
            entity.setOrderId(payExtJson.getString("orderId"));
            entity.setPurchaseToken(payExtJson.getString("purchaseToken"));
        }
        entity.setOrderNo(repayRequest.getOrderNo());
        entity.setPayOrderNo(String.valueOf(PaySnowflake.getInstance().nextId()));
        entity.setCallbackUrl(repayRequest.getCallbackUrl());
        entity.setMerchantCode("");
        entity.setPayGateway(repayRequest.getPayGateway());
        entity.setUserCode(repayRequest.getUserCode());
        entity.setProductCode(repayRequest.getProductCode());
        entity.setPayType(Integer.parseInt(repayRequest.getPayType()));
//        entity.setMerchantCode()
        entity.setAmount(new BigDecimal(repayRequest.getAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        entity.setStatus(PayStatusEnum.INIT.getCode());
//        entity.setRepayAccount();
        entity.setPayExt(repayRequest.getPayExt());
        pOrderDao.insert(entity);
        return entity;
    }


    private Event buildEvent(RepayRequest repayRequest) {
        return Event.builder().payLogActionEnum(PayLogActionEnum.REPAY)
                .orderNo(repayRequest.getOrderNo()).content(JSONObject.toJSONString(repayRequest)).build();
    }

    private BasicPayActionService getBasicPayActionService(RepayRequest repayRequest) {
        final PayGatewayEnum payType = PayGatewayEnum.getEnumByCode(repayRequest.getPayGateway());
        return payServiceFactory.buildPayProcess(payType);
    }


    @Override
    public Response<RepayStatusResponse> repayStatusQuery(RepayStatusRequest repayStatusRequest) {
        // 参数检查
        validateQueryStatusParams(repayStatusRequest);

        // 检查orderNo在p_order中是否存在记当，以避免重复支付操作
        final POrderEntity payOrder = queryByOrderNo(repayStatusRequest);
        if (Objects.isNull(payOrder)) {
            log.error("[PaymentLog-repayStatusQuery] ThePayOrderNotExists, userCode:{}, payOrderNo:{}",repayStatusRequest.getUserCode(), repayStatusRequest.getOrderNo());
            return Response.error("ThePayOrderNotExists, params=" + JSONObject.toJSONString(repayStatusRequest));

        }

        if (OrderStatus.PAID.getStatus() == payOrder.getStatus() || OrderStatus.CLOSE.getStatus() == payOrder.getStatus()) {
            log.info("[PaymentLog-repayStatusQuery] orderHasPaidOrClosed, userCode:{}, payOrderNo:{}, payOrderNo:{}", repayStatusRequest.getUserCode(), repayStatusRequest.getOrderNo(), payOrder.getPayOrderNo());
            final Response<RepayStatusResponse> r = Response.error("TheOrderHasPaidOrClosed");
            r.put("data", RepayStatusResponse.builder().orderNo(payOrder.getOrderNo()).status(PayStatusEnum.getEnumByCode(String.valueOf(payOrder.getStatus()))).build());
            return r;
        }

        repayStatusRequest.setPayOrderNo(payOrder.getPayOrderNo());
        final PayGatewayEnum payType = PayGatewayEnum.getEnumByCode(repayStatusRequest.getPayGateway());
        final BasicPayActionService basicPayActionService = payServiceFactory.buildPayProcess(payType);

        final RepayStatusResponse repayStatusResponse = basicPayActionService.repayStatusQuery(repayStatusRequest);
        if (Objects.isNull(repayStatusResponse)) {
            log.error("[PaymentLog-repayStatusQuery] InvokerRepayFailed, params={}" , JSONObject.toJSONString(repayStatusRequest));
            return Response.error("Invoker repay failed, params=" + JSONObject.toJSONString(repayStatusRequest));
        }
        return Response.ok(repayStatusResponse);
    }

    @Override
    public Response<String> queryExtentInfo(RepayRequest repayRequest) {
        final BasicPayActionService basicPayActionService = getBasicPayActionService(repayRequest);
        return Response.ok().putData(basicPayActionService.queryExtentInfo(repayRequest));
    }

    @Override
    public Response<Boolean> receiveNotify(ReceiveNotifyRequest repayStatusRequest) {
        PReceiveNotifyLogEntity entity = new PReceiveNotifyLogEntity();
        BeanUtils.copyProperties(repayStatusRequest, entity);
        receiveNotifyLogDao.insert(entity);
        return Response.ok(Boolean.TRUE);
    }

    @Override
    public Response<Boolean> storeOrderInfo(ReceiveNotifyRequest repayStatusRequest) {

        Map<String, Object> params = Maps.newHashMap();
        params.put("token", repayStatusRequest.getToken());
        if (!StringUtils.isEmpty(repayStatusRequest.getOrderNo())) {
            params.put("orderNo", repayStatusRequest.getOrderNo());
        }
        if (!StringUtils.isEmpty(repayStatusRequest.getOrderId())) {
            params.put("orderId", repayStatusRequest.getOrderId());
        }
        final List<PReceiveNotifyLogEntity> notifyLogs = receiveNotifyLogDao.selectByParams(params);
        if (CollectionUtils.isEmpty(notifyLogs)) {
            receiveNotify(repayStatusRequest);
            return Response.ok();
        }
        final List<PReceiveNotifyLogEntity> emptyOrderNoOrOrderIdRecords = notifyLogs.stream().filter(o -> StringUtils.isEmpty(o.getOrderId()) || StringUtils.isEmpty(o.getOrderNo())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(emptyOrderNoOrOrderIdRecords)) {
            return Response.ok();
        }
        for (PReceiveNotifyLogEntity entity : emptyOrderNoOrOrderIdRecords) {
            if (StringUtils.isEmpty(repayStatusRequest.getOrderId())) {
                entity.setOrderId(repayStatusRequest.getOrderId());
            }
            if (StringUtils.isEmpty(repayStatusRequest.getOrderNo())) {
                entity.setOrderNo(repayStatusRequest.getOrderNo());
            }
            receiveNotifyLogDao.updateByPrimaryKey(entity);
        }
        return Response.ok();


    }

    private void validateRepayParams(RepayRequest repayRequest) {
        ValidateParams.assertTrue(Objects.isNull(repayRequest), "The repay objects should not be empty");
        ValidateParams.notEmpty(repayRequest.getPayGateway(), "The param [payGateway] should not be empty");
        ValidateParams.notEmpty(repayRequest.getOrderNo(), "The param [orderNo] should not be empty");
        ValidateParams.notEmpty(repayRequest.getUserCode(), "The param [userCode] should not be empty");
        ValidateParams.notEmpty(repayRequest.getProductCode(), "The param [productCode] should not be empty");
    }

    private void validateQueryStatusParams(RepayStatusRequest repayRequest) {
        ValidateParams.notEmpty(repayRequest.getPayGateway(), "The param [payGateway] should not be empty");
        ValidateParams.notEmpty(repayRequest.getOrderNo(), "The param [orderNo] should not be empty");
        ValidateParams.notEmpty(repayRequest.getUserCode(), "The param [userCode] should not be empty");
    }

    @Override
    public Response<Boolean> addSubscriptionLog(ReceiveNotifyRequest receiveNotifyRequest) {
        if (receiveNotifyRequest == null || StringUtils.isEmpty(receiveNotifyRequest.getMessageId())) {
            return Response.ok(Boolean.FALSE);
        }

        final PSubscriptionLogEntity existsRecord = subscriptionLogDao.getPSubscriptionLogByMessageId(receiveNotifyRequest.getMessageId());
        if (existsRecord != null) {
            return Response.ok(Boolean.TRUE);
        }

        PSubscriptionLogEntity entity = new PSubscriptionLogEntity();
        entity.setOrderId(receiveNotifyRequest.getOrderId());
        entity.setMessageId(receiveNotifyRequest.getMessageId());
        entity.setReceiveNotifyMsg(receiveNotifyRequest.getReceiveNotifyMsg());
        entity.setReceiveTime(DateUtil.now());
        entity.setStatus(SubscriptionProcessStatus.INIT.getStatus());
        final PSubscriptionLogEntity addSuccessEntity = subscriptionLogDao.insert(entity);
        if (addSuccessEntity.getId() != null && addSuccessEntity.getId() > 0 ) {
            return Response.ok(Boolean.TRUE);
        }
        return Response.ok(Boolean.FALSE) ;
    }

    @Override
    public Response<Boolean> updateSubscriptionStatus(String messageId, SubscriptionProcessStatus status, String msg) {
        final int updateNum = subscriptionLogDao.updateSubscriptionStatus(messageId, status.getStatus(), msg);
        return Response.ok(updateNum > 0);
    }

    @Override
    public Response<Boolean> subscriptionSuccess(String orderId, String msg) {
        final int updateNum = subscriptionLogDao.subscriptionSuccess(orderId, msg);
        return Response.ok(updateNum > 0);
    }

    @Override
    public Response<Boolean> updateSubscription(String messageId, String token, String orderId) {
        final int updateNum = subscriptionLogDao.updateSubscription(messageId, token, orderId);
        return Response.ok(updateNum > 0);
    }
}
